package util

import (
	"archive/zip"
	"bufio"
	"bytes"
	"crypto/md5"
	"crypto/tls"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"golang.org/x/exp/rand"
)

// 得到文件的md5值
func GetFileMd5(filePath string) string {
	file, err := os.Open(filePath)
	if err != nil {
		return ""
	}
	defer file.Close()
	hash := md5.New()
	_, err = io.Copy(hash, file)
	if err != nil {
		return ""
	}
	return fmt.Sprintf("%x", hash.Sum(nil))
}

// 得到随机字符串,默认是20位
func GetRandUUID(length int) string {
	if length <= 0 {
		length = 20
	}
	str := "0123456789abcdefghijklmnopqrstuvwxyz"
	rand.Seed(uint64(time.Now().UnixNano()))
	var result strings.Builder
	characters := []rune(str)
	for i := 0; i < length; i++ {
		randomIndex := rand.Intn(len(characters))
		result.WriteRune(characters[randomIndex])
	}
	return result.String()
}

// 根据时间生成随机字符串，一般是前面时间yyyyMMddHHmmss后面的随机字符串长度为lenth
func GetRandDateUUID(lenth int) string {
	str := time.Now().Format("20060102150405")
	return str + GetRandUUID(lenth-len(str))
}

// 得到文件大小
func GetFileSize(filePath string) int64 {
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		return 0
	}
	return fileInfo.Size()
}

// 判断文件或者文件夹是否存在
func CheckExists(pathDir string) bool {
	_, er := os.Stat(pathDir)
	b := er == nil || os.IsExist(er)
	return b
}

// 判断文件是否存在
func CheckFileExists(pathDir string) bool {
	file, er := os.Stat(pathDir)
	b := er == nil || os.IsExist(er) && !os.IsNotExist(er)
	if file.IsDir() { // 如果是目录，则返回false
		return false
	}

	return b
}

// 判断是否文件夹
func CheckDirExists(pathDir string) bool {
	file, er := os.Stat(pathDir)
	b := er == nil || os.IsExist(er) && !os.IsNotExist(er)
	if file.IsDir() { // 如果是目录，则返回false
		return true
	}
	return b
}

// 复制文件,src:源文件路径,dst:目标文件路径
// 目标文件，如果文件夹不存在，自动创建目录
func CopyFile(src, dst string) (n int64, err error) {
	sourceFileStat, err := os.Stat(src)
	if err != nil {
		return 0, err
	}

	if !sourceFileStat.Mode().IsRegular() {
		return 0, fmt.Errorf("%s is not a regular file", src)
	}

	source, err := os.Open(src)
	if err != nil {
		return 0, err
	}
	defer source.Close()
	dir := filepath.Dir(dst)
	if CheckDirExists(dir) {
		Mkdir(dir)
	}
	destination, err := os.Create(dst)
	if err != nil {
		return 0, err
	}
	defer destination.Close()
	nBytes, err := io.Copy(destination, source)
	return nBytes, err
}

//创建多层目录，如果存在则不创建

func Mkdir(dir string) (e error) {
	_, er := os.Stat(dir)
	b := er == nil || os.IsExist(er)
	if !b {
		if err := os.MkdirAll(dir, 0775); err != nil {
			if os.IsPermission(err) {
				e = err
			}
		}
	}
	return
}

// 得到md5 32位
func GetMd5(m string) string {
	code := md5.New()
	code.Write([]byte(m))
	return hex.EncodeToString(code.Sum(nil))
}

// 把字节转换为字符串 B,Kb,Mb,Gb
func GetFormatBytes(total uint64) string {
	const unit = 1024
	if total < unit {
		return fmt.Sprintf("%d B", total)
	}
	div, exp := int64(unit), 0
	for n := total / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %c",
		float64(total)/float64(div), "KMGTPE"[exp])
}

// 检查是否IP规范
func CheckIp(ip string) bool {
	addr := net.ParseIP(ip)
	return addr != nil && !addr.IsLoopback()
}

// 是否数字
func CheckNumber(s string) bool {
	_, err := strconv.ParseFloat(s, 64)
	return err == nil
}

// 下载文件,大文件
func Download(url, filepath string) error {
	__times := 30 * time.Minute
	var client http.Client
	ps := "https://"
	if len(url) > len(ps) {
		if url[0:len(ps)] == ps { //是https
			tr := &http.Transport{
				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			}
			client = http.Client{Timeout: __times, Transport: tr}
		} else {
			client = http.Client{Timeout: __times}
		}
	} else {
		client = http.Client{Timeout: __times}
	}

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	req.Header.Set("User-Agent", "Mozilla/5.0")

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("bad status: %s", resp.Status)
	}
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	return err
}

// 得到当前执行文件的路径,如果是测试环境，返回"./",
// 路径后面带/
func GetPath() string {
	_path := ""
	if len(os.Args) > 0 {
		_path = os.Args[0]
	}
	if _path != "" {
		if strings.Contains(_path, "go-build") {
			//说明是测试环境
			return "./"
		}
		fileName := filepath.Base(_path)
		_path = _path[0 : len(_path)-len(fileName)]
		return _path
	}
	ex, err := os.Executable()
	if err != nil {
		return "./"
	}
	if strings.Contains(_path, "go-build") {
		//说明是测试环境
		return "./"
	}
	fileName := filepath.Base(ex)
	ex = ex[0 : len(ex)-len(fileName)]
	return ex
}

// 判断文件是否图片类型
func CheckImageAudit(inputFile string) (filetype string, rr error) {
	file, err := os.Open(inputFile)
	if err != nil {
		return
	}
	defer file.Close()

	buf := make([]byte, 12) // 足够检测常见图片格式
	reader := bufio.NewReader(file)
	n, err := reader.Read(buf)
	if err != nil || n < 12 {
		return
	}

	// 检查各种图片格式的Magic Number
	switch {
	case bytes.HasPrefix(buf, []byte{0xFF, 0xD8, 0xFF}): // JPEG
		return "jpeg", nil
	case bytes.HasPrefix(buf, []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}): // PNG
		return "png", nil
	case bytes.HasPrefix(buf, []byte{'G', 'I', 'F', '8'}): // GIF
		return "gif", nil
	case bytes.HasPrefix(buf, []byte{'B', 'M'}): // BMP
		return "bmp", nil
	case bytes.HasPrefix(buf, []byte{0x49, 0x49, 0x2A, 0x00}) || // TIFF (little endian)
		bytes.HasPrefix(buf, []byte{0x4D, 0x4D, 0x00, 0x2A}): // TIFF (big endian)
		return "big", nil
	case bytes.HasPrefix(buf, []byte{'R', 'I', 'F', 'F'}) && bytes.HasPrefix(buf[8:], []byte{'W', 'E', 'B', 'P'}): // WebP
		return "webp", nil
	default:
		return "", errors.New("文件不是有效的图片格式")
	}
}

// 判断文件是否zip类型
func CheckZipAudit(inputFile string) (filetype string, err error) {
	file, err := os.Open(inputFile)
	if err != nil {
		return
	}
	defer file.Close()

	buf := make([]byte, 4)
	reader := bufio.NewReader(file)
	_, err = reader.Read(buf)
	if err != nil {
		return
	}

	if bytes.Equal(buf, []byte{0x50, 0x4B, 0x03, 0x04}) {
		return "zip", nil
	}
	return "", errors.New("文件不是有效的压缩包格式")
}

// 判断文件是否zip
func CheckApkAudit(inputFile string) (filetype string, err error) {
	// 首先检查是否是ZIP文件
	_, err = CheckZipAudit(inputFile)
	if err != nil { // 使用前面定义的isZipFile函数
		return
	}

	// 打开ZIP文件检查内容
	r, err := zip.OpenReader(inputFile)
	if err != nil {
		return
	}
	defer r.Close()

	// 检查是否包含AndroidManifest.xml
	for _, f := range r.File {
		if f.Name == "AndroidManifest.xml" {
			return "apk", nil
		}
	}
	return "", errors.New("文件不是有效的APK格式")
}

// 判断文件是否为视频文件类型
func CheckVideoAudit(inputFile string) (fileType string, err error) {
	file, err := os.Open(inputFile)
	if err != nil {
		return
	}
	defer file.Close()

	buf := make([]byte, 16) // 足够检测常见视频格式
	reader := bufio.NewReader(file)
	n, err := reader.Read(buf)
	if err != nil || n < 16 {
		return
	}

	// 检查各种视频格式的Magic Number
	switch {
	// MP4 (ftyp)
	case bytes.HasPrefix(buf[4:], []byte("ftyp")):
		return "mp4", nil
	// AVI (RIFF)
	case bytes.HasPrefix(buf, []byte("RIFF")) && bytes.HasPrefix(buf[8:], []byte("AVI ")):
		return "avi", nil
	// MOV (ftyp)
	case bytes.HasPrefix(buf[4:], []byte("ftypqt")):
		return "mov", nil
	// FLV
	case bytes.HasPrefix(buf, []byte("FLV")):
		return "flv", nil
	// WebM
	case bytes.HasPrefix(buf, []byte{0x1A, 0x45, 0xDF, 0xA3}):
		return "webm", nil
	// MKV
	case bytes.HasPrefix(buf, []byte{0x1A, 0x45, 0xDF, 0xA3}):
		return "mkv", nil
	// MPEG
	case bytes.HasPrefix(buf, []byte{0x00, 0x00, 0x01, 0xBA}):
		return "mpeg", nil
	// 3GP
	case bytes.HasPrefix(buf[4:], []byte("ftyp3gp")):
		return "3gp", nil
	default:
		err = CheckTsAudit(inputFile)
		if err == nil {
			return "ts", nil
		}
		return
	}
}

// 判断文件是否为ts
func CheckTsAudit(inputFile string) (err error) {
	file, err := os.Open(inputFile)
	if err != nil {
		return
	}
	defer file.Close()

	buf := make([]byte, 188*2) // 足够检测常见视频格式
	reader := bufio.NewReader(file)
	n, err := reader.Read(buf)
	if err != nil || n < 188*2 {
		return fmt.Errorf("文件不是有效的视频格式ts")
	}
	if buf[0] == 0x47 || buf[188] == 0x47 {
		return nil
	}
	return errors.New("文件不是有效的视频格式")
}

// 判断是否为文档类型
func CheckDocAudit(inputFile string) (filetype string, err error) {
	file, err := os.Open(inputFile)
	if err != nil {
		return
	}
	defer file.Close()

	buf := make([]byte, 16) // 足够检测常见文档格式
	reader := bufio.NewReader(file)
	n, err := reader.Read(buf)
	if err != nil || n < 16 {
		return
	}
	return "", nil
}
